using System;
using System.Collections.Generic;
using System.Text;
using System.Collections.Specialized;
using RaisingStudio.Common.Xml;
using RaisingStudio.Data.Common.Configuration;
using System.Xml;
using System.Collections;
using RaisingStudio.Common.Reflection;

namespace RaisingStudio.Data.Common.Managers
{
    public sealed class DefinitionManager
    {
        #region Instance
        //public static readonly DefinitionManager Instance = new DefinitionManager();
        private static volatile DefinitionManager _instance = null;
        public static DefinitionManager Instance
        {
            get
            {
                if (_instance == null)
                {
                    lock (typeof(DefinitionManager))
                    {
                        if (_instance == null)
                        {
                            _instance = new DefinitionManager();
                        }
                    }
                }
                return _instance;
            }
        }
        #endregion

        private const string DEFINITIONS_FILE_NAME = "definitions.config";

        private const string XML_ALIAS = "definitionsConfig/aliases/alias";
        private const string XML_DEFINITION = "definitionsConfig/definitions/definition";

        private HybridDictionary _aliases = new HybridDictionary();

        //public HybridDictionary Aliases
        //{
        //    get { return _aliases; }
        //    set { _aliases = value; }
        //}

        private Dictionary<System.Type, string> _aliasesDefinitionNames = new Dictionary<System.Type, string>();

        //public Dictionary<System.Type, string> AliasesDefinitionNames
        //{
        //    get { return _aliasesDefinitionNames; }
        //    set { _aliasesDefinitionNames = value; }
        //}


        private HybridDictionary _definitions = new HybridDictionary();

        //public HybridDictionary Definitions
        //{
        //    get { return _definitions; }
        //    set { _definitions = value; }
        //}

        private DefinitionManager()
        {
            this._aliases.Clear();
            this._definitions.Clear();
            GetDefinitions();
        }

        private void InitializeAliasesTypes()
        {
            this._aliasesDefinitionNames.Clear();
            foreach (DictionaryEntry dictionaryEntry in this._aliases)
            {
                Alias alias = dictionaryEntry.Value as Alias;
                if (alias != null)
                {
                    try
                    {
                        alias.Initialize();
                    }
                    catch (Exception ex)
                    {
                        System.Diagnostics.Debug.WriteLine(ex);
                    }
                    Type type = alias.Type;
                    if (type != null)
                    {
                        this._aliasesDefinitionNames.Add(type, alias.Name as string);
                    }
                }
            }
        }

        private object definitions_lock = new object();

        public void AppendDefinitions(string baseDirectory)
        {
            XmlDocument xmlProviders = Resources.GetConfigAsXmlDocument(baseDirectory, DEFINITIONS_FILE_NAME);
            AddDefinitions(xmlProviders);
        }

        private void GetDefinitions()
        {
            XmlDocument xmlProviders = Resources.GetConfigAsXmlDocument(DEFINITIONS_FILE_NAME);
            AddDefinitions(xmlProviders);
        }

        private void AddDefinitions(XmlDocument xmlProviders)
        {
            if ((xmlProviders != null) && (xmlProviders.DocumentElement != null))
            {
                lock (this.definitions_lock)
                {
                    Alias alias;
                    foreach (XmlNode node in xmlProviders.SelectNodes(XML_ALIAS))
                    {
                        alias = AliasDeSerializer.Deserialize(node);
                        this._aliases.Add(alias.TypeName, alias);
                    }

                    Definition definition;
                    foreach (XmlNode node in xmlProviders.SelectNodes(XML_DEFINITION))
                    {
                        definition = DefinitionDeSerializer.Deserialize(node);
                        this._definitions.Add(definition.Name, definition);
                    }

                    // Resolve aliases types.
                    InitializeAliasesTypes();
                }
            }
        }

        public Definition GetDefinition(string name)
        {
            lock (this.definitions_lock)
            {
                return this._definitions[name] as Definition;
            }
        }

        public bool DefinitionExists(string name)
        {
            lock (this.definitions_lock)
            {
                return (this._definitions[name] != null);
            }
        }

        public bool DefinitionExists(System.Type type)
        {
            lock (this.definitions_lock)
            {
                if (this._aliasesDefinitionNames.ContainsKey(type))
                {
                    return true;
                }
                else
                {
                    string typeName = GetTypeName(type);
                    if (this._aliases[typeName] != null)
                    {
                        Alias alias = this._aliases[typeName] as Alias;
                        if (alias != null)
                        {
                            string definitionName = alias.Name;
                            return (this._definitions[definitionName] != null);
                        }
                    }
                }
                return false;
            }
        }

        private string GetDefinitionName(Type type)
        {
            lock (this.definitions_lock)
            {
                if (this._aliasesDefinitionNames.ContainsKey(type))
                {
                    return this._aliasesDefinitionNames[type];
                }
                else
                {
                    string typeName = GetTypeName(type);
                    string definitionName = (this._aliases[typeName] as Alias).Name;
                    return definitionName;
                }
            }
        }

        private string GetTypeName(System.Type type)
        {
            return type.AssemblyQualifiedName;
        }

        public System.Xml.XmlElement LoadXmlDocument(string definitionName)
        {
            Definition definition = this._definitions[definitionName] as Definition;
            if (definition != null)
            {
                if (definition.XmlElement != null)
                {
                    return definition.XmlElement;
                }
                else
                {
                    System.Xml.XmlElement xmlElement = null;
                    if (!string.IsNullOrEmpty(definition.Resource))
                    {
                        System.Xml.XmlDocument xmlDocument = Resources.GetConfigAsXmlDocument(definition.Resource);
                        xmlElement = DefinitionNameAttribute.LoadXmlDocument(definitionName, xmlDocument);
                    }
                    else if (!string.IsNullOrEmpty(definition.Url))
                    {
                        System.Xml.XmlDocument xmlDocument = Resources.GetUrlAsXmlDocument(definition.Url);
                        xmlElement = DefinitionNameAttribute.LoadXmlDocument(definitionName, xmlDocument);
                    }
                    else if (string.IsNullOrEmpty(definition.Embedded))
                    {
                        System.Xml.XmlDocument xmlDocument = Resources.GetEmbeddedResourceAsXmlDocument(definition.Embedded);
                        xmlElement = DefinitionNameAttribute.LoadXmlDocument(definitionName, xmlDocument);
                    }
                    definition.XmlElement = xmlElement;
                    return xmlElement;
                }
            }
            else
            {
                // TODO: exception.
            }
            return null;
        }

        public System.Xml.XmlElement LoadXmlDocument(Type type)
        {
            DefinitionNameAttribute definitionNameAttribute = Attribute.GetCustomAttribute(type, typeof(DefinitionNameAttribute)) as DefinitionNameAttribute;
            if (definitionNameAttribute != null)
            {
                if (!string.IsNullOrEmpty(definitionNameAttribute.FileName))
                {
                    string fileName = definitionNameAttribute.FileName;
                    string name = definitionNameAttribute.Name;
                    return DefinitionNameAttribute.LoadXmlDocument(type, fileName, name, false);
                }
                else
                {
                    string name = definitionNameAttribute.Name;
                    return LoadXmlDocument(type, name);
                }
            }
            else
            {
                if (DefinitionExists(type))
                {
                    string definitionName = GetDefinitionName(type);
                    return LoadXmlDocument(definitionName);
                }
                else
                {
                    return DefinitionNameAttribute.LoadXmlDocument(type);
                }
            }
        }

        public System.Xml.XmlElement LoadXmlDocument(Type type, string name)
        {
            if (DefinitionExists(name))
            {
                return LoadXmlDocument(name);
            }
            else
            {
                return DefinitionNameAttribute.LoadXmlDocument(type, name);
            }
        }
    }
}
